package com.nvwa.coding.util.num;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 一个可以将中文数字转为阿拉伯数字的工具类
 */
public class ChineseNumberConverter {
    /**
     * 中文数字
     */
    private static final String[] CHN_NUMBER = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
    private static final String[] CHN_NUMBER2 = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
    /**
     * 权位
     */
    private static final String[] CHN_UNIT = {"", "十", "百", "千"};
    /**
     * 节权位
     */
    private static final String[] CHN_UNIT_SECTION = {"", "万", "亿", "万亿"};
    /**
     * 中文权位名称对应的阿拉伯数字
     */
    private static final ChineseUnitNameValue[] CHN_NAME_VALUE = {
            new ChineseUnitNameValue("十", 10, false),
            new ChineseUnitNameValue("百", 100, false),
            new ChineseUnitNameValue("千", 1000, false),
            new ChineseUnitNameValue("万", 10000, true),
            new ChineseUnitNameValue("亿", 100000000, true)
    };
    private static final Map<String, Integer> CHN_NUMBER_VALUE_MAPPING;
    private static final Map<String, Integer> CHN_NAME_VALUE_MAPPING;

    static {
        CHN_NUMBER_VALUE_MAPPING = new HashMap<>(16);
        for (int i = 0; i < CHN_NUMBER.length; i++) {
            CHN_NUMBER_VALUE_MAPPING.put(CHN_NUMBER[i], i);
        }

        CHN_NAME_VALUE_MAPPING = new HashMap<>(8);
        for (ChineseUnitNameValue chineseUnitNameValue : CHN_NAME_VALUE) {
            CHN_NAME_VALUE_MAPPING.put(chineseUnitNameValue.name, chineseUnitNameValue.value);
        }
    }

    /**
     * 阿拉伯数字转换为中文数字的核心算法实现。
     *
     * @param number 为需要转换为中文数字的阿拉伯数字
     * @return 阿拉伯数字转换为中文数字
     */
    public static String toChineseNumber(int number) {
        StringBuilder chineseNumber = new StringBuilder();
        boolean negativeNumber = number < 0;
        boolean needZero = false;
        long calcNum = number;
        if (negativeNumber) {
            calcNum = Math.abs(calcNum);
        }
        // 节权位的位置
        int pos = 0;
        if (calcNum == 0) {
            //如果num为0，进行特殊处理。
            chineseNumber.insert(0, CHN_NUMBER[0]);
        }
        while (calcNum > 0) {
            int section = (int) (calcNum % 10000);
            if (needZero) {
                chineseNumber.insert(0, CHN_NUMBER[0]);
            }
            String sectionToChn = sectionNumToChn(section);
            //判断是否需要节权位
            sectionToChn += (section != 0) ? CHN_UNIT_SECTION[pos] : CHN_UNIT_SECTION[0];
            chineseNumber.insert(0, sectionToChn);
            //判断section中的千位上是不是为零，若为零应该添加一个零。
            needZero = (section < 1000 && section > 0);
            pos++;
            calcNum = calcNum / 10000;
        }
        if (negativeNumber) {
            chineseNumber.insert(0, "负");
        }
        return chineseNumber.toString();
    }

    /**
     * 中文数字字符串所对应的int类型的阿拉伯数字
     *
     * @param chineseNumber 中文数字字符串
     * @return 中文数字对应的阿拉伯数字
     */
    public static int toNumber(String chineseNumber) {
        int returnNumber = 0;
        int section = 0;
        int pos = 0;
        int number = 0;
        boolean negativeNumber = chineseNumber.startsWith("负");
        if (negativeNumber) {
            chineseNumber = chineseNumber.substring(1);
        }
        while (pos < chineseNumber.length()) {
            int num = chnNumToValue(chineseNumber.substring(pos, pos + 1));
            // 若num>=0，代表该位置（pos），所对应的是数字不是权位。若小于0，则表示为权位
            if (num >= 0) {
                number = num;
                pos++;
                // pos是最好一位，直接将number加入到section中。
                if (pos >= chineseNumber.length()) {
                    section += number;
                    returnNumber += section;
                    break;
                }
            } else {
                int chnNameValueIndex = chnUnitToValue(chineseNumber.substring(pos, pos + 1));
                if (CHN_NAME_VALUE[chnNameValueIndex].secUnit) {
                    // 表示该位置所对应的权位是节权位，
                    section = (section + number) * CHN_NAME_VALUE[chnNameValueIndex].value;
                    returnNumber += section;
                    section = 0;
                } else {
                    section += number * CHN_NAME_VALUE[chnNameValueIndex].value;
                }
                pos++;
                number = 0;
                if (pos >= chineseNumber.length()) {
                    returnNumber += section;
                    break;
                }
            }
        }
        return negativeNumber ? -returnNumber : returnNumber;
    }


    /**
     * 将四位的section转换为中文数字
     *
     * @param section 节权位值
     * @return 对应的中文数字
     */
    private static String sectionNumToChn(int section) {
        StringBuilder returnStr = new StringBuilder();
        // 节权位的位置编号，0-3依次为个十百千
        int unitPos = 0;

        boolean zero = true;
        while (section > 0) {

            int v = (section % 10);
            if (v == 0) {
                if (!zero) {
                    // 需要补0，zero的作用是确保对连续的多个0，只补一个中文零
                    zero = true;
                    returnStr.insert(0, CHN_NUMBER[v]);
                }
            } else {
                // 至少有一个数字不是0
                zero = false;
                // 数字v所对应的中文数字
                StringBuilder tempStr = new StringBuilder(CHN_NUMBER[v]);
                // 数字v所对应的中文权位
                tempStr.append(CHN_UNIT[unitPos]);
                returnStr.insert(0, tempStr);
            }
            // 移位
            unitPos++;
            section = section / 10;
        }
        return returnStr.toString();
    }

    /**
     * 返回中文数字汉字所对应的阿拉伯数字，若str不为中文数字，则返回-1
     */
    private static int chnNumToValue(String str) {
        return Optional.ofNullable(CHN_NUMBER_VALUE_MAPPING.get(str))
                .orElse(-1);
    }

    /**
     * 返回中文汉字权位在chnNameValue数组中所对应的索引号，若不为中文汉字权位，则返回-1
     */
    private static int chnUnitToValue(String str) {
        return Optional.ofNullable(CHN_NAME_VALUE_MAPPING.get(str))
                .orElse(-1);
    }

    /**
     * 中文转换成阿拉伯数字，中文字符串除了包括0-9的中文汉字，还包括十，百，千，万等权位。
     * 此处是完成对这些权位的类型定义。
     */
    private static class ChineseUnitNameValue {
        /**
         * 权位的中文名称
         */
        public final String name;
        /**
         * 权位对应的数值
         */
        public final int value;
        /**
         * 该权位是否为节权位，即万，亿，万亿等
         */
        public final Boolean secUnit;

        public ChineseUnitNameValue(String name, int value, Boolean secUnit) {
            this.name = name;
            this.value = value;
            this.secUnit = secUnit;
        }
    }
}
